package mail;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Properties;

import javax.mail.Authenticator;
import javax.mail.BodyPart;
import javax.mail.FetchProfile;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.NoSuchProviderException;
import javax.mail.Part;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.UIDFolder;
import javax.mail.FetchProfile.Item;
import javax.mail.UIDFolder.FetchProfileItem;
import javax.mail.internet.MimeMessage;
import javax.mail.search.AndTerm;
import javax.mail.search.FlagTerm;
import javax.mail.search.MessageIDTerm;
import javax.mail.search.SearchTerm;
import javax.swing.JOptionPane;
import javax.swing.ProgressMonitor;
import javax.swing.SwingWorker;

import com.sun.mail.pop3.POP3Folder;
import com.sun.mail.pop3.POP3Store;

import mail.bean.AccountBean;
import mail.bean.EmailBean;
import mail.impl.NewMailTerm;
import mail.impl.PwdAuthenticator;
import mail.util.StringUtil;

public class MailSession {
	private Session session = null;
	private POP3Store store = null;
	private POP3Folder inbox = null;
	private Properties env = null;
	private AccountBean acct = null;
	private ProgressMonitor progress = null;
	private MailReceiver worker = null;
	private MultiEmail top = null;
	private String inboxFolder = null;
	
	public MailSession(AccountBean account, MultiEmail top) {
		env = account.getEnv();
		acct = account;
		this.top = top;
		inboxFolder = acct.getSaveFolder()+ "/" + acct.INBOX;
    new File(inboxFolder).mkdir();
    
	}
	
	public Session getSession() {
		if(session == null) {
			session = Session.getInstance(env, new PwdAuthenticator(acct));
		}
		return session;
	}
	
	public Store getStore() {
		if(session != null) {
			if(store == null) {
				try {
					store = (POP3Store)session.getStore();
				} catch (NoSuchProviderException e) {
					e.printStackTrace();
				}
			}
			return store;
		}else {
			return null;
		}
	}
	
	public void close() {
	  if(inbox != null) {
      try {
        inbox.close(true);
        inbox = null;
      } catch (MessagingException e1) {
        e1.printStackTrace();
      }
    }
	  if(store != null) {
			try {
				store.close();
				store = null;
			} catch (MessagingException e) {
				e.printStackTrace();
			}
		}
		
	}
	
	public void receiveMailBackgroup() {
	  new Thread() {
	    public void run() {
	      receiveMails();
	    }
	  }.start();
	}
	
	public boolean openInbox() {
	  try {
	    progress.setProgress(0);
      progress.setNote("Initialize...");
      getSession();
      getStore();
      progress.setProgress(1);
      progress.setNote("Connecting to store...");
      store.connect();
      progress.setProgress(3);
      progress.setNote("Store connected.");
      inbox = (POP3Folder)store.getFolder(AccountBean.INBOX);
      inbox.open(Folder.READ_ONLY);
      progress.setNote("Opening INBOX folder...");
      progress.setProgress(4);
      progress.setNote("INBOX folder opened.");
      return true;
    } catch (Exception e) {
      e.printStackTrace();
      progress.close();
      close();
      return false;
    }
	}
	
	public void receiveMails() {
		try {  
		  new Thread() {
        public void run() {
          progress = new ProgressMonitor(top,"Wait for connecting to email server " + 
              acct.getAccountAddress(), null, 0, 5);
          progress.setMillisToDecideToPopup(0);
          progress.setMillisToPopup(0);
        }
		  }.start();
		  while(true) {
		    if(progress != null && !progress.isCanceled()) {
		      break;
		    }
		    Thread.sleep(10);
		  }
		  if(!openInbox()) {
		    JOptionPane.showMessageDialog(top, "Connect to Email Server " + 
            acct.getAccountAddress() + " failed", "Alert", JOptionPane.ERROR_MESSAGE);		    
		    return ;
		  }
		  progress.setNote("Query the count of unreceived mail");      
		  SearchTerm st = new NewMailTerm(acct.getUidList(), inbox);
		  Message[] msgs = inbox.search(st);
		  progress.setProgress(5);
		  progress.close();
		  if(msgs.length == 0) {
        JOptionPane.showMessageDialog(top, "No new mail found", "Alert", JOptionPane.WARNING_MESSAGE);  
        close();
        return ;
      }
			progress = new ProgressMonitor(top, "Wait for receiving " + msgs.length + " emails: ", 
			    null, 0, 100);
			progress.setMillisToDecideToPopup(1);
			progress.setMillisToPopup(1);
			worker = new MailReceiver(msgs);
		 
    	worker.execute();
  
      new Thread() {
        public void run() {
          try {
            while (true) {
              if (progress.isCanceled()) {
                worker.cancel(true);
                top.getListPanel().getModel().setEmailList(acct.getMailList());
                top.getListPanel().getTable().updateUI();
                progress.close();
                break;
              } else if (worker.isDone()) {
                top.getListPanel().getModel().setEmailList(acct.getMailList());
                top.getListPanel().getTable().updateUI();
                progress.close();
                break;
              } else if (worker.isCancelled()) {
                top.getListPanel().getModel().setEmailList(acct.getMailList());
                top.getListPanel().getTable().updateUI();
                progress.close();
                break;
              } else {
                progress.setProgress(worker.getProgress());
                progress.setNote(worker.getProgress() + "% emails received.");
              }
              Thread.sleep(100);           
            }
          } catch (InterruptedException e) {
            e.printStackTrace();
            worker.cancel(true);
            top.getListPanel().getModel().setEmailList(acct.getMailList());
            top.getListPanel().getTable().updateUI();
            progress.close();
          } 
        }
      }.start();
			
		} catch (Exception e) {
      e.printStackTrace();
    } finally {
      if(progress != null) {
        progress.close();
      }
    }
	}
	
	public static void main(String[] args) {
		
	}
	
	public void saveFile(String fileName, InputStream is) {
	  FileOutputStream fos = null;
	  
	  try {
      File tmp = new File(fileName);
      tmp.createNewFile();
      fos = new FileOutputStream(tmp);
      byte[] data = new byte[1024];
      int len;
      while((len = is.read(data)) > -1) {
        fos.write(data, 0, len);
      }
    } catch (Exception e) {
      System.out.println("FileName:" + fileName);
      e.printStackTrace();
    } finally {
      if(fos != null) {
        try {
          fos.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
	}
	
	public void handleMultiPart(Multipart part, EmailBean bean) {
	  try {
	  int bodyCount = part.getCount();
	  BodyPart body = null;
	  for(int i=0; i<bodyCount; i++) {
	    body = part.getBodyPart(i);
	    if(body.isMimeType("text/*")) {
	      bean.setContent(body.getContent().toString());
	      bean.setContentType(body.getContentType());
	    }else if(body.getDisposition() != null && (body.getDisposition().
	        equals(Part.ATTACHMENT) || body.getDisposition().equals(Part.INLINE))) {
	      String fileName = body.getFileName();
	      fileName = StringUtil.getFileName(fileName);
	      String path = inboxFolder + "/" + bean.getUid() + "_" + 
	          fileName + "_a";
	      bean.getAttach().put(fileName, path);
	      saveFile(path, body.getInputStream());
	    } else{
	      if(body.getContent() instanceof Multipart) {
	        handleMultiPart((Multipart) body.getContent(), bean);
	      }
	    }
	    
	  }
	  }catch(Exception e) {
	    e.printStackTrace();
	  }
	}
	
	class MailReceiver extends SwingWorker<Void, Integer> {
	  private Message[] msgs = null;
	  public MailReceiver(Message[] msgs) {
	    this.msgs = msgs;
	  }
	  
	  protected Void doInBackground() throws Exception {
	    ObjectOutputStream oop = null;
	    ArrayList<EmailBean> mailList = acct.getMailList();
	    ArrayList<String> uidList = acct.getUidList();
	    try {
	      if(mailList == null) {
	        mailList = new ArrayList<EmailBean>();
	        acct.setMailList(mailList);
	        uidList = new ArrayList<String>();
	        acct.setUidList(uidList);
	      }
        EmailBean bean = null;
        int i=1;
        int size = msgs.length;
        FetchProfile fp = new FetchProfile();
        fp.add(FetchProfile.Item.ENVELOPE);
        fp.add(FetchProfile.Item.CONTENT_INFO);
        fp.add(UIDFolder.FetchProfileItem.UID);
        Message[] ft = new Message[1];
        Object obj = null;
        File tmp = null;
        for(Message msg : msgs) {
          if(this.isCancelled()) {
            break;
          }
          
          ft[0] = msg;
          inbox.fetch(ft, fp);
          msg = ft[0];
          bean = new EmailBean();
          bean.setUid(inbox.getUID(msg));
          bean.setReceiveDate(msg.getReceivedDate());
          bean.setSendDate(msg.getSentDate());
          if(msg.getFrom()[0] != null) {
            bean.setSender(StringUtil.getFrom(msg.getFrom()[0].toString()));
          }
          bean.setSize(msg.getSize() + "byte");
          bean.setSubject(msg.getSubject());
          uidList.add(bean.getUid());
          obj = msg.getContent();
          if(!msg.isMimeType("text/*")) {
            handleMultiPart((Multipart)msg.getContent(), bean);
          }else {
            bean.setContent(obj.toString());
            bean.setContentType(msg.getContentType());
          }
          mailList.add(bean);
          tmp = new File (inboxFolder + "/" + bean.getUid() + ".mail");
          tmp.createNewFile();
          oop = new ObjectOutputStream(new FileOutputStream(tmp));
          oop.writeObject(bean);
          oop.close();
          oop = null;
          setProgress((i++)*100/size);
        }
        return null;
      } catch (Exception e) {
	      e.printStackTrace();
	      cancel(true);
	      JOptionPane.showMessageDialog(top, "Error occurs", "Alert", JOptionPane.ERROR_MESSAGE);
	      return null;
	    } finally {
	      close();
	      if (oop != null) {
	        oop.close();
	      }
	    }
    }
	}
}

